package com.blueSky.lesson026;

import java.util.concurrent.*;

public class MyThreadPoolExecutor extends ThreadPoolExecutor {
    private ThreadLoaclContext threadLoaclContext ;
    public MyThreadPoolExecutor(ThreadLoaclContext context,int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        this.threadLoaclContext = context;
    }

    public MyThreadPoolExecutor(ThreadLoaclContext context, int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,
                                ThreadFactory threadFactory, RejectedExecutionHandler handler){
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        this.threadLoaclContext = context;
    }

    @Override
    public void execute(Runnable command) {
        super.execute(this.new RunableWrap(command));
    }
    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return super.submit(this.new CallableWrap(task));
    }

    public interface ThreadLoaclContext<T> {
        /**
         * 获取本地变量上下文接口，用于解决线程池中共享外部线程ThreadLocal数据的问题
         * @return
         */
        T getContext();

        /**
         * 设置上下文，线程池中的线程执行任务的时候会调用此方法，将上下文设置到线程中
         * @param context {@link #getContext()} 返回的对象
         */
        void setContext(T context);
    }

    public class CallableWrap<V> implements Callable<V> {
        private Callable<V> target;

        private Object context;

        public CallableWrap(Callable<V> target) {
            this.target = target;
            this.context = MyThreadPoolExecutor.this.threadLoaclContext.getContext();
        }

        @Override
        public V call() throws Exception {
            MyThreadPoolExecutor.this.threadLoaclContext.setContext(context);
            return this.target.call();
        }
    }

    public class RunableWrap implements Runnable {
        private Runnable target;
        private Object context;
        public RunableWrap(Runnable target) {
            this.target = target;
            this.context = MyThreadPoolExecutor.this.threadLoaclContext.getContext();
        }
        @Override
        public void run() {
            MyThreadPoolExecutor.this.threadLoaclContext.setContext(context);
            this.target.run();
        }
    }

}
